/*
 * #%L
 * SwiftUICommonUtilities
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2012 Linkwithweb
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
/**
 * 
 */
package com.linkwithweb.products.swiftui.spring.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;

/**
 * Used to generate Metadata of all Columns of Given SQL and used to control the
 * results
 * 
 * @author ashwin
 * 
 */
public class BatchResultSetExtractor implements ResultSetExtractor {
	private static final transient Log logger = LogFactory
			.getLog(BatchResultSetExtractor.class);
	/**
	 * The constant indicating that a column does not allow <code>NULL</code> values.
	 */
	int columnNoNulls = 0;

	/**
	 * The constant indicating that a column allows <code>NULL</code> values.
	 */
	int columnNullable = 1;

	/**
	 * The constant indicating that the nullability of a column's values is
	 * unknown.
	 */
	int columnNullableUnknown = 2;
	private String entityName = null;

	private final RowMapper rowMapper;

	private final int rowsExpected;

	private DatabaseResult databaseResult = new DatabaseResult();

	/**
	 * @return the result
	 */
	public DatabaseResult getDatabaseResult() {
		return databaseResult;
	}

	/**
	 * @param result
	 *            the result to set
	 */
	public void setDatabaseResult(DatabaseResult result) {
		this.databaseResult = result;
	}

	/**
	 * @return the entityName
	 */
	public String getEntityName() {
		return entityName;
	}

	/**
	 * @param entityName
	 *            the entityName to set
	 */
	public void setEntityName(String entityName) {
		this.entityName = entityName;
	}

	/**
	 * Create a new RowMapperResultSetExtractor.
	 * 
	 * @param rowMapper
	 *            the RowMapper which creates an object for each row
	 */
	public BatchResultSetExtractor(RowMapper rowMapper) {
		this(rowMapper, 0);
	}

	/**
	 * Create a new RowMapperResultSetExtractor.
	 * 
	 * @param rowMapper
	 *            the RowMapper which creates an object for each row
	 * @param rowsExpected
	 *            the number of expected rows (just used for optimized
	 *            collection handling)
	 */
	public BatchResultSetExtractor(RowMapper rowMapper, int rowsExpected) {
		Assert.notNull(rowMapper, "RowMapper is required");
		this.rowMapper = rowMapper;
		this.rowsExpected = rowsExpected;
	}

	public Object extractData(ResultSet rs) throws SQLException {
		List results = (this.rowsExpected > 0 ? new ArrayList(this.rowsExpected)
				: new ArrayList());
		int rowNum = 0;
		// queryContext
		int totalRowsProcessed = 0;
		try {

			try {
				generateMetaData(rs);
			} catch (Exception e) {
				logger.debug("Problem In generation of Metadata :", e);
			}

			System.out.println("Getting Batch Size :");
			int batchSize = 1000;
			// Batch object for every Batch size and Push them to target DB
			try {
				/*
				 * batchSize = Integer.parseInt(PropertyUtils.getConfig()
				 * .getProperty("batchSize"));
				 */
			} catch (Exception e) {
				logger.debug("Unable to read Batch Size", e);
				e.printStackTrace();
			}

			List<String> batchSqlArray = new ArrayList<String>();

			while (rs != null && rs.next()) {
				Object returnObject = this.rowMapper.mapRow(rs, rowNum++);
				if (returnObject != null) {
					totalRowsProcessed = totalRowsProcessed + 1;
					// Get Batch Update Statement Here and Add Errored Out sql
					// to Log
					results.add(returnObject);
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			databaseResult.setData(results);
		}
		return results;
	}

	/**
	 * @param rs
	 * @throws SQLException
	 */
	private void generateMetaData(ResultSet rs) throws SQLException {
		int columnCount = rs.getMetaData().getColumnCount();
		DBEntityComponent entityComponent = TableMetaDataCache.getInstance()
				.getMetaData(rs.getMetaData().getTableName(1));

		entityComponent = null;
		entityName = rs.getMetaData().getTableName(1);
		// For First Row Create Metadata
		if (entityComponent == null) {
			entityComponent = new DBEntityComponent();

			entityComponent.setEntityDescription(entityName);
			entityComponent.setEntityName(entityName);

			// Used to make sure no column Names match
			Map<String, Integer> presentColumnNames = new HashMap<String, Integer>();
			for (int column = 1; column <= columnCount; column++) {
				ColumnMetadata columnMetadata = new ColumnMetadata();

				int nullableStatus = rs.getMetaData().isNullable(column);
				if (nullableStatus == columnNoNulls) {
					columnMetadata.setNullable(false);
				} else if (nullableStatus == columnNullable) {
					columnMetadata.setNullable(true);
				} else if (nullableStatus == columnNoNulls) {
					columnMetadata.setNullable(false);
				}

				columnMetadata.setCatalogName(rs.getMetaData().getCatalogName(
						column));
				columnMetadata.setTableName(rs.getMetaData().getTableName(
						column));
				columnMetadata.setCatalogName(rs.getMetaData().getCatalogName(
						column));

				columnMetadata.setColumnClassName(rs.getMetaData()
						.getColumnClassName(column));

				columnMetadata.setColumnLabel(rs.getMetaData().getColumnLabel(
						column));

				String presColumnName = rs.getMetaData().getColumnName(column);

				if (presentColumnNames.containsKey(presColumnName)) {
					int colCount = (Integer) presentColumnNames
							.get(presColumnName);
					presColumnName = presColumnName + "_" + colCount;
					presentColumnNames.put(
							rs.getMetaData().getColumnName(column),
							colCount + 1);
				} else {
					presentColumnNames.put(
							rs.getMetaData().getColumnName(column), 0);
				}
				columnMetadata.setColumnName(presColumnName);
				
				if(StringUtils.equalsIgnoreCase(presColumnName, "RowNumber")){
					continue;
				}

				String dbColumnTypeName = rs.getMetaData().getColumnTypeName(
						column);
				dbColumnTypeName = dbColumnTypeName.toUpperCase();

				// System.out.println("Datatype For Columns name :"+presColumnName
				// + " is "+dbColumnTypeName);
				columnMetadata.setColumnTypeName(dbColumnTypeName);

				columnMetadata.setColumnType(rs.getMetaData().getColumnType(
						column));

				int displaySize = rs.getMetaData().getColumnDisplaySize(column);

				if (StringUtils.contains(columnMetadata.getColumnTypeName(),
						"char")) {
					if (displaySize > 2000) {
						// LONGCHAR
						columnMetadata.setColumnTypeName("LONGCHAR");
					} 
				} 
				columnMetadata.setColumnDisplaySize(displaySize);

				columnMetadata.setPrecision(rs.getMetaData().getPrecision(
						column));
				columnMetadata.setScale(rs.getMetaData().getScale(column));
				columnMetadata.setSchemaName(rs.getMetaData().getSchemaName(
						column));
				columnMetadata.setSigned(rs.getMetaData().isSigned(column));
				columnMetadata.setTableName(rs.getMetaData().getTableName(
						column));

				entityComponent.addColumnMetadata(column, columnMetadata);
			}
			TableMetaDataCache.getInstance().storeMetaData(
					entityComponent.getEntityName(), entityComponent);

		}
		databaseResult.setMetadata(TableMetaDataCache.getInstance()
				.getMetaData(getEntityName()));

		databaseResult.getMetadata().setEntityName(entityName);
	}

	/**
	 * @param format
	 * @param date
	 * @return
	 */
	public static String getFormattedDate(String format, Date date) {
		SimpleDateFormat formatter = new SimpleDateFormat(format);
		String newDateString = formatter.format(date);
		return newDateString;
	}

}
